﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using DomainDriver.DomainModeling.Constraints;
using DomainDriver.DomainModeling.DataProviders;
using DomainDriver.DomainModeling.DomainModels;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.Exceptions;
using DomainDriver.DomainModeling.Repositories;

namespace DomainDriver.DomainModeling.GenericUnitTesting.Constraints
{
    public abstract class ParentChildConstraintTestFixture<DOMAIN_MODEL, PARENT_KEY, CHILD_KEY, PARENT, CHILD> : ConstraintTestFixture<DOMAIN_MODEL, ParentChildConstraint<PARENT_KEY, CHILD_KEY, PARENT, CHILD>>
        where DOMAIN_MODEL : class, IDomainModel
        where PARENT : IKeyedDomainObject<PARENT_KEY, PARENT>
        where CHILD : IKeyedDomainObject<CHILD_KEY, CHILD>
    {
        public ParentChildConstraintTestFixture(DataSourcePersistenceType persistenceType)
            : base(persistenceType)
        {
            // do nothing
        }

        protected virtual IRepository<CHILD_KEY, CHILD> ChildRepository
        {
            get { return DomainModel.GetRepository<CHILD_KEY, CHILD>(); }
        }

        protected virtual IRepository<PARENT_KEY, PARENT> ParentRepository
        {
            get { return DomainModel.GetRepository<PARENT_KEY, PARENT>(); }
        }

        protected abstract PARENT CreateParent(ref bool parentAlreadyAdded);
        protected abstract PARENT CreateFakeParent();
        protected abstract CHILD CreateChild();
        protected abstract void AssignParentToChild(PARENT_KEY parentKey, CHILD child);

        [Test]
        public virtual void VerifyConstraintOnAddingChild_POSITIVE()
        {
            bool parentAlreadyAdded = false;
            PARENT parent = CreateParent(ref parentAlreadyAdded);
            PARENT parentCopy = default(PARENT);
            CHILD child = CreateChild();
            CHILD childCopy = default(CHILD);

            childCopy = child.Copy();
            AssignParentToChild(parent.Key, child);
            Assert.AreNotEqual(childCopy, child);

            parentCopy = parent.Copy();
            childCopy = child.Copy();

            if (!parentAlreadyAdded)
            { ParentRepository.Add(parent); }
            ChildRepository.Add(child);

            SaveAndReloadDomainModel();

            parent = ParentRepository.Read(parent.Key);
            child = ChildRepository.Read(child.Key);

            Assert.AreEqual(parentCopy, parent);
            Assert.AreEqual(childCopy, child);
        }

        [Test]
        [ExpectedException(typeof(DomainObjectManagementException))]
        public virtual void VerifyConstraintOnAddingChild_NEGATIVE()
        {
            PARENT parent = CreateFakeParent();
            CHILD child = CreateChild();
            CHILD childCopy = default(CHILD);

            childCopy = child.Copy();
            AssignParentToChild(parent.Key, child);
            Assert.AreNotEqual(childCopy, child);

            // SKIP: ParentRepository.Add(parent);
            ChildRepository.Add(child);
        }

        [Test]
        public virtual void VerifyConstraintOnUpdatingChild_POSITIVE()
        {
            bool parent1AlreadyAdded = false;
            bool parent2AlreadyAdded = false;
            PARENT parent1 = CreateParent(ref parent1AlreadyAdded);
            PARENT parent1Copy = default(PARENT);
            PARENT parent2 = CreateFakeParent();
            PARENT parent2Copy = default(PARENT);
            CHILD child = CreateChild();
            CHILD childCopy = default(CHILD);

            Assert.AreNotEqual(parent1, parent2);

            childCopy = child.Copy();
            AssignParentToChild(parent1.Key, child);
            Assert.AreNotEqual(childCopy, child);

            if (!parent1AlreadyAdded)
            { ParentRepository.Add(parent1); }
            ChildRepository.Add(child);

            parent1Copy = parent1.Copy();
            childCopy = child.Copy();

            SaveAndReloadDomainModel();

            parent1 = ParentRepository.Read(parent1.Key);
            child = ChildRepository.Read(child.Key);

            Assert.AreEqual(parent1Copy, parent1);
            Assert.AreEqual(childCopy, child);

            child = ChildRepository.Read(child.Key);
            childCopy = child.Copy();
            AssignParentToChild(parent2.Key, child);
            Assert.AreNotEqual(childCopy, child);

            if (!parent2AlreadyAdded)
            { ParentRepository.Add(parent2); }
            ChildRepository.Update(child);

            parent1Copy = parent1.Copy();
            parent2Copy = parent2.Copy();
            childCopy = child.Copy();

            SaveAndReloadDomainModel();

            parent1 = ParentRepository.Read(parent1.Key);
            parent2 = ParentRepository.Read(parent2.Key);
            child = ChildRepository.Read(child.Key);

            Assert.AreEqual(parent1Copy, parent1);
            Assert.AreEqual(parent2Copy, parent2);
            Assert.AreEqual(childCopy, child);
        }

        [Test]
        [ExpectedException(typeof(DomainObjectManagementException))]
        public virtual void VerifyConstraintOnUpdatingChild_NEGATIVE()
        {
            bool parent1AlreadyAdded = false;
            PARENT parent1 = CreateParent(ref parent1AlreadyAdded);
            PARENT parent1Copy = default(PARENT);
            PARENT parent2 = CreateFakeParent();
            CHILD child = CreateChild();
            CHILD childCopy = default(CHILD);

            Assert.AreNotEqual(parent1, parent2);

            childCopy = child.Copy();
            AssignParentToChild(parent1.Key, child);
            Assert.AreNotEqual(childCopy, child);

            if (!parent1AlreadyAdded)
            { ParentRepository.Add(parent1); }
            ChildRepository.Add(child);

            parent1Copy = parent1.Copy();
            childCopy = child.Copy();

            SaveAndReloadDomainModel();

            parent1 = ParentRepository.Read(parent1.Key);
            child = ChildRepository.Read(child.Key);

            Assert.AreEqual(parent1Copy, parent1);
            Assert.AreEqual(childCopy, child);

            child = ChildRepository.Read(child.Key);
            childCopy = child.Copy();
            AssignParentToChild(parent2.Key, child);
            Assert.AreNotEqual(childCopy, child);

            // SKIP: ParentRepository.Add(parent2);
            ChildRepository.Update(child);
        }

        [Test]
        public virtual void VerifyConstraintOnDeletingParent_POSITIVE()
        {
            bool parentAlreadyAdded = false;
            PARENT parent = CreateParent(ref parentAlreadyAdded);
            PARENT parentCopy = default(PARENT);
            CHILD child = CreateChild();
            CHILD childCopy = default(CHILD);

            childCopy = child.Copy();
            AssignParentToChild(parent.Key, child);
            Assert.AreNotEqual(childCopy, child);

            if (!parentAlreadyAdded)
            { ParentRepository.Add(parent); }
            ChildRepository.Add(child);

            parentCopy = parent.Copy();
            childCopy = child.Copy();

            SaveAndReloadDomainModel();

            parent = ParentRepository.Read(parent.Key);
            child = ChildRepository.Read(child.Key);

            Assert.AreEqual(parentCopy, parent);
            Assert.AreEqual(childCopy, child);

            ChildRepository.Remove(child.Key);
            ParentRepository.Remove(parent.Key);

            SaveAndReloadDomainModel();

            Assert.IsFalse(ParentRepository.Exists(parent.Key));
            Assert.IsFalse(ChildRepository.Exists(child.Key));
        }

        [Test]
        [ExpectedException(typeof(DomainObjectManagementException))]
        public virtual void VerifyConstraintOnDeletingParent_NEGATIVE()
        {
            bool parentAlreadyAdded = false;
            PARENT parent = CreateParent(ref parentAlreadyAdded);
            PARENT parentCopy = default(PARENT);
            CHILD child = CreateChild();
            CHILD childCopy = default(CHILD);

            childCopy = child.Copy();
            AssignParentToChild(parent.Key, child);
            Assert.AreNotEqual(childCopy, child);

            if (!parentAlreadyAdded)
            { ParentRepository.Add(parent); }
            ChildRepository.Add(child);

            parentCopy = parent.Copy();
            childCopy = child.Copy();

            SaveAndReloadDomainModel();

            parent = ParentRepository.Read(parent.Key);
            child = ChildRepository.Read(child.Key);

            Assert.AreEqual(parentCopy, parent);
            Assert.AreEqual(childCopy, child);

            // SKIP: ChildRepository.Remove(child.Key);
            ParentRepository.Remove(parent.Key);
        }
    }
}